package com.tyk.bookstore.front.product.service.impl;


import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tyk.bookstore.front.common.cache.anno.ICache;
import com.tyk.bookstore.front.common.cache.constant.CacheConstant;
import com.tyk.bookstore.front.common.cache.manager.CacheManager;
import com.tyk.bookstore.front.common.core.model.query.SuggestionQuery;
import com.tyk.bookstore.front.common.core.model.vo.PageVo;
import com.tyk.bookstore.front.common.core.util.AssertUtil;
import com.tyk.bookstore.front.common.core.util.ObjUtil;
import com.tyk.bookstore.front.common.dal.mapper.dto.SinDto;
import com.tyk.bookstore.front.common.security.util.SecurityUtil;
import com.tyk.bookstore.front.product.manager.ProductManager;
import com.tyk.bookstore.front.product.model.entity.*;
import com.tyk.bookstore.front.product.model.enume.BookState;
import com.tyk.bookstore.front.product.model.query.BookCollectPageQuery;
import com.tyk.bookstore.front.product.model.result.BookResultEnum;
import com.tyk.bookstore.front.product.model.vo.BookPageVo;
import com.tyk.bookstore.front.product.model.vo.BookVo;
import com.tyk.bookstore.front.product.repository.*;
import com.tyk.bookstore.front.product.service.BookService;
import com.tyk.bookstore.front.product.util.MapStructProduct;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigInteger;
import java.util.List;

/**
 * @author tyk
 * @description 书籍管理
 */
@Service
@RequiredArgsConstructor
public class BookServiceImp implements BookService {

    private final BookRepository bookRepository;
    private final BookStatisticRepository bookStatisticRepository;
    private final PublisherRepository publisherRepository;
    private final BookCollectRepository bookCollectRepository;
    private final BookPraiseRepository bookPraiseRepository;
    private final DisplayImageRepository displayImageRepository;
    private final AuthorRepository authorRepository;
    private final TransactionTemplate transactionTemplate;
    private final ProductManager productManager;
    private final CacheManager cacheManager;
    private final MapStructProduct mapStructProduct;

    @Value("${mine.cache.redis.bloom.filters[0].name}")
    private String bloomFilterName;

    @PostConstruct
//    @Scheduled(cron = CacheConstant.BOOK_BLOOM_FILTER_SCHEDULE_CRON)
    protected void initBloomFilter() {
        List<Long> idList = bookRepository.<BigInteger>selectSinList(
                BookDo::getId, BookDo.class
        ).stream().map(it -> it.getV1().longValue()).toList();
        cacheManager.reset(bloomFilterName, idList);
    }


    @Override
    public BookVo getById(Long id) {
        AssertUtil.isTrue(
                cacheManager.contains(bloomFilterName, id),
                BookResultEnum.BOOK_NOT_FOUND_OR_OFF
        );
        BookService bookService = (BookService) (AopContext.currentProxy());
        BookVo vo = bookService.getByIdFromDb(id);
        AssertUtil.notNull(vo, BookResultEnum.BOOK_NOT_FOUND_OR_OFF);
        // 补充点赞和收藏信息
        if (StpUtil.isLogin()) {
            Long memberId = SecurityUtil.getLoginId();
            Boolean collected = bookCollectRepository.exist2Eq(
                    BookCollectDo::getMemberId, memberId,
                    BookCollectDo::getBookId, id,
                    BookCollectDo.class
            );
            Boolean praised = bookPraiseRepository.exist2Eq(
                    BookPraiseDo::getMemberId, memberId,
                    BookPraiseDo::getBookId, id,
                    BookPraiseDo.class
            );
            vo.setCollected(collected).setPraised(praised);
        }
        return vo;
    }

    @Override
    @ICache(
            prefix = CacheConstant.PRODUCT_DETAIL_PREFIX,
            expireTime = CacheConstant.PRODUCT_DETAIL_EXPIRE_TIME,
            lockPrefix = CacheConstant.PRODUCT_DETAIL_LOCK_PREFIX
    )
    public BookVo getByIdFromDb(Long id) {
        // 校验书籍是否存在
        AssertUtil.isTrue(
                bookRepository.exist2Eq(
                        BookDo::getId, id,
                        BookDo::getState, BookState.ON,
                        BookDo.class
                ),
                BookResultEnum.BOOK_NOT_FOUND
        );

        // 查询书籍信息
        BookDo book = bookRepository.selectById(id);
        BookStatisticDo bookStatistic = bookStatisticRepository.selectById(id);
        BookVo bv = mapStructProduct.toBookVo(book, bookStatistic);

        // 作者姓名
        bv.setAuthorName(
                authorRepository.<String>selectSin1Eq(
                        AuthorDo::getName,
                        AuthorDo::getId, bv.getAuthorId(),
                        AuthorDo.class
                ).getV1()
        );

        // 出版社名称
        bv.setPublisherName(
                publisherRepository.<String>selectSin1Eq(
                        PublisherDo::getName,
                        PublisherDo::getId, bv.getPublisherId(),
                        PublisherDo.class
                ).getV1()
        );

        // 分类全称
        bv.setClassificationName(
                productManager.buildClazzFullName(book.getClassificationId())
        );

        // 各级分类信息
        bv.setClassificationNameList(
                productManager.buildClazzNameVoList(book.getClassificationId())
        );

        // 展示图片
        bv.setImgList(
                displayImageRepository.<String>selectSinList1Eq(
                        DisplayImageDo::getPath,
                        DisplayImageDo::getBookId, bv.getId(),
                        DisplayImageDo.class
                ).stream().map(SinDto::getV1).toList()
        );

        // 返回书籍详情
        return bv;
    }

    @Override
    public BookPageVo getCollectPage(BookCollectPageQuery query) {

        Long memberId = StpUtil.getLoginIdAsLong();

        // 查询当前会员被收藏的书籍的ID（含分页）
        Page<BookCollectDo> page = new Page<>(query.getPage(), query.getLimit());
        LambdaQueryWrapper<BookCollectDo> w = Wrappers.lambdaQuery(BookCollectDo.class);
        w.select(BookCollectDo::getBookId)
                .eq(BookCollectDo::getMemberId, memberId)
                .orderBy(true, true, BookCollectDo::getCreateTime);
        List<Long> idList = bookCollectRepository
                .selectPage(page, w)
                .getRecords()
                .stream().map(BookCollectDo::getBookId)
                .toList();

        // 特判（防止后续IN条件为空）
        if (ObjUtil.isEmpty(idList)) return PageVo.instance(BookPageVo.class);

        // 查询书籍
        List<BookPageVo.Book> records = productManager.getBookPageVo$BookList(idList);
        productManager.fillCollectedAndPraised(records);

        // 返回数据
        BookPageVo bpv = new BookPageVo();
        bpv.setTotal(page.getTotal())
                .setCurrent(page.getCurrent())
                .setSize(page.getSize())
                .setRecords(records);

        return bpv;
    }

    @Override
    @ICache(
            prefix = CacheConstant.BOOK_SUGGESTION_PREFIX,
            expireTime = CacheConstant.BOOK_SUGGESTION_EXPIRE_TIME,
            lockPrefix = CacheConstant.BOOK_SUGGESTION_LOCK_PREFIX
    )
    public List<String> getSuggestionList(SuggestionQuery query) {
        return bookRepository
                .<String>selectSinList1Eq1Like(
                        BookDo::getName,
                        BookDo::getState, BookState.ON,
                        BookDo::getTitle, query.getNameLike(),
                        BookDo.class
                )
                .stream().map(SinDto::getV1).toList();
    }


    @Override
    public void collectOrCancel(Long bookId) {
        Long memberId = StpUtil.getLoginIdAsLong();
        transactionTemplate.executeWithoutResult(status -> {
            AssertUtil.isTrue(
                    bookRepository.exist1Eq(BookDo::getId, bookId, BookDo.class),
                    BookResultEnum.BOOK_NOT_FOUND
            );
            Boolean f = bookCollectRepository.exist1Eq(BookCollectDo::getBookId, bookId, BookCollectDo.class);
            if (f) {
                bookCollectRepository.delete2Eq(
                        BookCollectDo::getBookId, bookId,
                        BookCollectDo::getMemberId, memberId,
                        BookCollectDo.class
                );
            } else bookCollectRepository.insert(new BookCollectDo(bookId, memberId));
        });
    }


    @Override
    public void praiseOrCancel(Long bookId) {
        Long memberId = StpUtil.getLoginIdAsLong();
        transactionTemplate.executeWithoutResult(status -> {
            AssertUtil.isTrue(
                    bookRepository.exist1Eq(BookDo::getId, bookId, BookDo.class),
                    BookResultEnum.BOOK_NOT_FOUND
            );
            Boolean f = bookPraiseRepository.exist1Eq(BookPraiseDo::getBookId, bookId, BookPraiseDo.class);
            if (f) {
                bookPraiseRepository.delete2Eq(
                        BookPraiseDo::getBookId, bookId,
                        BookPraiseDo::getMemberId, memberId,
                        BookPraiseDo.class
                );
            } else bookPraiseRepository.insert(new BookPraiseDo(bookId, memberId));
        });
    }


}
